import base64
import cgi
import logging
import math
import os
import re
import urllib
import gdata.alt.appengine
import gdata.photos.service
from google.appengine.api import images
from google.appengine.api import memcache
from google.appengine.api import urlfetch
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from xml.dom import minidom

client_width = 240
client_height = 320
RE_SE_NETFRONT = re.compile(r"sonyericsson(.*)netfront", re.I)

#______________________________________________________________________________
def fetch_url(url):
    """ Fetch the content located at an url """

    fetch_result = urlfetch.fetch(url, allow_truncated=True)

    if fetch_result.status_code != 200:
        self.response.out.write('Error ' + fetch_result.status_code)
        return False

    if fetch_result.content_was_truncated:
        self.response.out.write('Response too large: ')
        self.response.out.write(len(rss_fetch_result.content))
        return False

    return fetch_result.content

#______________________________________________________________________________
def get_text(nodelist):
    """ Return the content of the text nodes """

    rc = ""
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            rc = rc + node.data
    return rc
    
#______________________________________________________________________________
def get_user_prefs():
    user = users.get_current_user()
    if user:
        user_prefs = UserPrefs.get_by_key_name('key:' + user.user_id())
        return user_prefs
    else:
        return None
    
#______________________________________________________________________________
def get_gd_client():
    gd_client = gdata.photos.service.PhotosService()
    gdata.alt.appengine.run_on_appengine(gd_client)
    user_prefs = get_user_prefs()
    if user_prefs and user_prefs.picasa_token:
        gd_client.SetAuthSubToken(user_prefs.picasa_token)
    return gd_client
  
#______________________________________________________________________________
class UserPrefs(db.Model):
    user = db.UserProperty()
    auto_rotate = db.BooleanProperty()
    picasa_token = db.StringProperty()
    
#______________________________________________________________________________
class MainPageHandler(webapp.RequestHandler):
    """ Main page handler """
    
    def is_navigator_valid(self):
        user_agent = os.environ['HTTP_USER_AGENT']
        return bool(RE_SE_NETFRONT.search(user_agent))
        
    def get(self):
    
        my_photos = ''
        user = users.get_current_user()
        if user:
            login_text = 'Welcome, ' + user.nickname() + '! (<a href="' + users.create_logout_url('/') + '">sign out</a>)'
            user_prefs = UserPrefs.get_by_key_name('key:' + user.user_id())
            # If no user prefs were found, create some
            if not user_prefs:
                user_prefs = UserPrefs(key_name='key:' + user.user_id(), user=user)
                user_prefs.put()
            # If a picasa token has been sent to this page, store it in the user prefs
            picasa_token = self.request.get('token')  
            if picasa_token:
                # Upgrade to a session token
                gd_client = gdata.photos.service.PhotosService()
                gdata.alt.appengine.run_on_appengine(gd_client)
                gd_client.SetAuthSubToken(picasa_token)
                gd_client.UpgradeToSessionToken()
                # Store it
                user_prefs.picasa_token = gd_client.GetAuthSubToken()
                user_prefs.put()
            # If no picasa token has been created, offer to create one
            elif not user_prefs.picasa_token:
                gd_client = gdata.photos.service.PhotosService()
                gdata.alt.appengine.run_on_appengine(gd_client)
                login_text = login_text + """<br><a href="%s">Connect to Picasa Web Album</a>.""" % gd_client.GenerateAuthSubURL(self.request.url, 'http://picasaweb.google.com/data/', False, True)
            else:
                my_photos = '<p><a href="/g?u=' + user.nickname() + '">View my photos</a></p>'
        else:
            # User not logged in
            login_text = '<a href="' + users.create_login_url("/") + '">Sign in or register</a>.'

        navigator_is_valid = self.is_navigator_valid()
        
        path = os.path.join(os.path.dirname(__file__), 'main.html')
        self.response.out.write(template.render(path, {'navigator_is_valid': navigator_is_valid, 'login_text': login_text, 'my_photos':my_photos}))

#______________________________________________________________________________
class GalleryHandler(webapp.RequestHandler):
    """ Gallery handler """

    def get(self):
        gallery_user = self.request.get('u')

        gd_client = get_gd_client()
        if gd_client and gd_client.GetAuthSubToken():
            albums = gd_client.GetUserFeed(user=gallery_user).entry
            tags = gd_client.GetFeed('/data/feed/api/user/%s?kind=tag' % gallery_user).entry
        else:
            rss_url = 'http://picasaweb.google.com/data/feed/base/user/' + gallery_user + '?alt=rss&kind=album'
            rss = fetch_url(rss_url)
            if not rss:
                return

            class Album:
                pass

            dom = minidom.parseString(rss)
            items = dom.getElementsByTagName('item')
            albums = []
            for item in items:
                url = get_text(item.getElementsByTagName('guid')[0].childNodes)
                album = Album()
                album.gphoto_id = gdata.photos.PhotosBaseElement()
                album.gphoto_id.text = url[url.rfind('/')+1:url.find('?')]
                album.title = gdata.photos.PhotosBaseElement()
                album.title.text = get_text(item.getElementsByTagName('title')[0].childNodes)
                albums.append(album)
            dom.unlink()
            
            tags = []

        path = os.path.join(os.path.dirname(__file__), 'gallery.html')
        self.response.out.write(template.render(path, {'albums': albums, 'user': gallery_user, 'tags': tags}))


#______________________________________________________________________________
class AlbumHandler(webapp.RequestHandler):
    """ Album handler """

    def get(self):
        gallery_user = self.request.get('u')
        albumid = self.request.get('id')
        authkey = self.request.get('ak')

        gd_client = get_gd_client()
        if gd_client and gd_client.GetAuthSubToken():
            photos = gd_client.GetFeed('/data/feed/api/user/%s/albumid/%s?kind=photo' % (gallery_user, albumid))
            images = []
            for photo in photos.entry:
                url = photo.content.src
                url = url[:url.rfind('/')] + '/s800' + url[url.rfind('/'):]
                images.append(urllib.quote(base64.b64encode(url)))
        else:
            rss_url = 'http://picasaweb.google.com/data/feed/base/user/' + gallery_user + '/albumid/' + albumid + '?alt=rss&kind=photo'
            if authkey:
                rss_url += '&authkey=' + authkey

            rss = fetch_url(rss_url)
            if not rss:
                return

            dom = minidom.parseString(rss)
            enclosures = dom.getElementsByTagName('enclosure')
            images = []
            for enclosure in enclosures:
                url = enclosure.getAttribute('url')
                url = url[:url.rfind('/')] + '/s800' + url[url.rfind('/'):]
                images.append(urllib.quote(base64.b64encode(url)))
            dom.unlink()

        path = os.path.join(os.path.dirname(__file__), 'album.html')
        self.response.out.write(template.render(path, {'images': images}))

#______________________________________________________________________________
class TagHandler(webapp.RequestHandler):
    """ Album handler """

    def get(self):
        gallery_user = self.request.get('u')
        tag = self.request.get('t')

        gd_client = get_gd_client()
        if gd_client and gd_client.GetAuthSubToken():
            photos = gd_client.GetTaggedPhotos(tag, user=gallery_user)
            images = []
            for photo in photos.entry:
                url = photo.content.src
                url = url[:url.rfind('/')] + '/s800' + url[url.rfind('/'):]
                images.append(urllib.quote(base64.b64encode(url)))

        path = os.path.join(os.path.dirname(__file__), 'album.html')
        self.response.out.write(template.render(path, {'images': images}))

#______________________________________________________________________________
class ImageHandler(webapp.RequestHandler):
    """ Image Handler """

    def get_image(self, image_id):
        """ Get image data from the cache or an url """

        image_data = memcache.get(image_id, 'ImageData')
        if image_data is not None:
            logging.info('Image retrieved from cache: ' + image_id)
            return image_data
        else:
            url = base64.b64decode(urllib.unquote(image_id))
            fetch_result = urlfetch.fetch(url)

            if fetch_result.status_code != 200:
                self.response.out.write('Error %i  while retrieving %s' % (fetch_result.status_code, url))
                return None
            else:
                memcache.add(image_id, fetch_result.content, namespace='ImageData')
                logging.info('Image stored in cache: ' + image_id)
                return fetch_result.content


    def get(self, image_id):

        image_content = self.get_image(image_id)
        if image_content is None:
            return

        zoom = float(self.request.get('z', 1))
        display_height = int(client_height * zoom)
        display_width = int(client_width * zoom)

        img = images.Image(image_content)
        if img.width > img.height:
            img.rotate(90)
        img.resize(width=display_width)

        self.response.headers["Content-Type"] = "image/jpeg"
        self.response.out.write(img.execute_transforms(output_encoding=images.JPEG))



#______________________________________________________________________________
application = webapp.WSGIApplication([('/', MainPageHandler),
                                      ('/g', GalleryHandler),
                                      ('/a', AlbumHandler),
                                      ('/t', TagHandler),
                                      ('/i/(.*)', ImageHandler),
                                     ],
                                     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()
